package strs

import (
	"fmt"
	"github.com/tidwall/gjson"
	"strconv"
	"strings"
)

type SlowLogAnalysis struct {
	 // 操作类型
	 OpType string `json:"op_type"`
	 // 用来去重的唯一键
	 UniqueKey string `json:"unique_key"`
}

type OpType string

const (
	OP_FIND         		 OpType = "find"
	OP_FIND_AND_MODIFY_      OpType = "findAndModify"
	UPDATE      			 OpType = "update"
)

/**
	从日志的context部分，把关于操作的信息封装成json。以便后续的关键字提取。
	关键步骤：
		1.使用括号匹配算法，匹配最外层的大括号。遇到第一个'{'就进栈，当栈空的时候，就是一个完整的类Json字符串。
		2.因为在类Json中key和部分的value都没有引号，故为了完成这个功能，需要做以下几步。
		3.当遇到 '{'的时候，需要在后面加上 "
		4.遇到 ':'的时候，需要在前面和后面加上 '"'，但是需要考虑一种特殊情况，在value字段中如果
		遇到了类似于IP这样的字段，此时不能加。需要把这种情况排除，因此设置了一个maohaoCan标志位，
		5.当遇到'，'的时候，当逗号之前不是 '"'或者'}'或者']'的时候，在逗号前面加上 '"',如果后面没有'"',就
		在后面加上'"'.也需要注意一种在value中包括，的情况，主要是针对Timestamp(1578865559,1)，此时需要做一个
		简单的判断，判断一下此时栈中的括号。
		6.当遇到 '}'的时候，如果左边的字符不是 '"'或者 '}'或者']'就给其加上 '"'。
	以上。具体特殊情况再做分析调整。
 */
func FormatStringToJson(log string) *SlowLogAnalysis {
	brackets := map[rune]rune{')': '(', ']': '[', '}': '{'}
	var stack []rune
	start := false
	// 标志位，冒号前后可否加 "
	maohaoCan:= true
	var ans = make([]byte,0)
	log = strings.Replace(log, " ", "", -1)
	for index, char := range log {
		if char == '{' {
			start = true
		}
		if !start {
			continue
		}
		// { 后 : 前面 , 后面加上 "，可以给key加上引号。但是还无法解决TimeStamp的问题
		if char == '{' {
			ans = append(ans, byte(char))
			ans = append(ans, byte('"'))
			maohaoCan = true;
			//needToAddChar = true
			// 处理: 的时候需要注意，可能会把IP这种给分开。
		}else if char == ':' {

			if maohaoCan {
				if index >=1 && log[index - 1] != '"' {
					ans = append(ans,byte('"'))
				}

				ans = append(ans, byte(char))
				// 如果 : 之后的那个字符没有" ，也要加上
				if index < len(log) && log[index + 1] != '"' && log[index + 1] != '{' && log[index + 1] != '['{
					ans = append(ans,byte('"'))

				}

			}else {
				ans = append(ans, byte(char))
			}
			// ans = append(ans, byte(char))

			maohaoCan = false
		} else if char == ',' {
			maohaoCan = true
			// 判断栈中是否有未匹配的小括号，如果有，不添加。
			if stack[len(stack) - 1] != '(' {
				// 如果 ， 之前有 '"', '}',']'就不添加。
				if index >= 1 && log[index - 1] != '"' && log[index - 1] != '}' && log[index - 1] != ']'{
					ans = append(ans, byte('"'))
				}
				ans = append(ans, byte(char))
				// 如果,的下一个字符不是"，就加上，
				if index < len(log) && log[index + 1] != '"' {
					ans = append(ans, byte('"'))
				}

			}else {
				ans = append(ans, byte(char))
			}
		}else if char == '}'{
			if index >= 1 && log[index - 1] != '"' && log[index - 1] != '}' && log[index - 1] != ']'{
				ans = append(ans, byte('"'))
			}
			ans = append(ans, byte(char))

		} else {
			ans = append(ans, byte(char))
		}

		if char == '(' || char == '{' || char == '[' {
			stack = append(stack, char)
		} else if brackets[char] == stack[len(stack) - 1] {
			stack = stack[:len(stack) - 1]
		}else {
			fmt.Errorf("error")
		}

		if len(stack) == 0 {
			break
		}
	}
	return ParseJson(string(ans))
}

/**
	从操作的Json中提取出来需要的字段。利用gjson库。
	可以做定制，未定义的操作为 others。
	设置hasFind 是为了寻找操作属性（find , update , findAndModify）的时候只做一次搜索。
	因为findAndModify中包括update，因此先做findAndModify的搜索。

 */
func ParseJson(content string) *SlowLogAnalysis {
	res := &SlowLogAnalysis{
		OpType:    "others",
		UniqueKey: "",
	}
	hasFind := false // 标志位，表示只能寻找一次关键词。
	// find关心里面的filter
	find := gjson.Get(content, "find")
	// findAndModify关心里面的 query
	findAndModify := gjson.Get(content, "findAndModify")
	// update关心里面的updates里面的q
	update := gjson.Get(content, "update")

	// 如果是findAndModify,关心query
	if findAndModify.Exists() && !hasFind{
		// get query
		query := gjson.Get(content,"query")
		res.OpType = "findAndModify"
		res.UniqueKey = query.String()
		hasFind = true
	}
	// 处理关于find 的 条件 filter 。
	//从find中找到filter
	if find.Exists() && !hasFind{
		// get filter
		filter := gjson.Get(content,"filter")
		res.OpType = "find"
		res.UniqueKey = filter.String()
		hasFind = true
	}
	// 如果是update ，关心updates中的q
	if update.Exists() && !hasFind{
		updates := gjson.Get(content,"updates")
		res.OpType = "update"
		re:= updates.Array()
		for _, v := range re {
			// 获取q
			q := v.Get("q").String()
			res.UniqueKey = q
			break
		}
		hasFind = true
	}
	return res
}


func ParseSlowLog(log string) *map[string]interface{} {
	res := make(map[string]interface{}, 0)
	var context string
	fields := strings.Fields(strings.TrimSpace(log))
	size := len(fields)
	dbColIndex := 9999
	middleBracketsIndex := 3
	for i, field := range fields {
		if i == 0 {
			res["ts"] = field
		} else if i == middleBracketsIndex {
			// 这里判断 [replSetDistLockPinger]
			if field[len(field) - 1] == ']' {
				dbColIndex = middleBracketsIndex + 2;
			}else {
				middleBracketsIndex++;
			}
		} else if i == dbColIndex {
			// 对
			dbAndCol := strings.Split(field, ".")
			if len(dbAndCol) != 2 {
				continue
			}
			res["db"] = dbAndCol[0]
			res["col"] = dbAndCol[1]
		} else if i == size-2 {
			continue
		} else if i == size-1 {
			field = field[:len(field)-2]
			delay, err := strconv.Atoi(field)
			if err != nil {
				continue
			}
			res["delay"] = delay
		} else {
			context += field
		}
		if i > 0 && i < size-2 {
			context += " "
		}
	}
	// 解析json。添加两个字段，
	ParseContextResult := FormatStringToJson(context)
	res["context"] = context
	res["opType"] = ParseContextResult.OpType
	res["UniqueKey"] =  res["db"].(string) + "-" + res["col"].(string) + "-" + ParseContextResult.UniqueKey
	return &res
}